package is.glif.fenius.translators.idc;

import is.glif.fenius.api.FActivationStatus;
import is.glif.fenius.api.FAvailReq;
import is.glif.fenius.api.FAvailResp;
import is.glif.fenius.api.FCancelReq;
import is.glif.fenius.api.FCancelResp;
import is.glif.fenius.api.FCreateReq;
import is.glif.fenius.api.FCreateResp;
import is.glif.fenius.api.FListReq;
import is.glif.fenius.api.FListResp;
import is.glif.fenius.api.FExternalFault;
import is.glif.fenius.api.FInternalFault;
import is.glif.fenius.api.FQueryReq;
import is.glif.fenius.api.FQueryResp;
import is.glif.fenius.api.FReservation;
import is.glif.fenius.api.FReservationReqParams;
import is.glif.fenius.api.FReservationStatus;
import is.glif.fenius.api.Translator;

import java.util.ArrayList;
import java.util.Date;


import net.es.oscars.client.improved.cancel.CancelClient;
import net.es.oscars.client.improved.create.CreateClient;
import net.es.oscars.client.improved.create.CreateRequestParams;
import net.es.oscars.client.improved.list.ListClient;
import net.es.oscars.client.improved.query.QueryClient;
import net.es.oscars.wsdlTypes.CreateReply;
import net.es.oscars.wsdlTypes.ListReply;
import net.es.oscars.wsdlTypes.PathInfo;
import net.es.oscars.wsdlTypes.ResCreateContent;
import net.es.oscars.wsdlTypes.ResDetails;

import org.apache.log4j.Logger;
import org.ogf.schema.network.topology.ctrlplane.CtrlPlaneHopContent;
import org.ogf.schema.network.topology.ctrlplane.CtrlPlaneLinkContent;
import org.ogf.schema.network.topology.ctrlplane.CtrlPlanePathContent;
import org.ogf.schema.network.topology.ctrlplane.CtrlPlaneSwcapContent;
import org.ogf.schema.network.topology.ctrlplane.CtrlPlaneSwitchingCapabilitySpecificInfo;



public class IDCTranslator implements Translator {
    private final static Logger log = Logger.getLogger(IDCTranslator.class);
    static class theLock extends Object {
    }
    static public theLock lockObject = new theLock();

    public FCreateResp create(FCreateReq r) throws FInternalFault, FExternalFault {

        log.debug("Create received");
        this.loadConfig();
        FReservationReqParams resvParams = r.getResvParams();
        Long startTime = 0L;
        Long endTime = 0L;


        Date startDate = resvParams.getActivationTime();
        startTime = startDate.getTime() / 1000L; // turn ms into seconds
        endTime = startTime + resvParams.getDuration();

        CreateRequestParams rparams = new CreateRequestParams();
        rparams.setBandwidth(resvParams.getBandwidth());
        rparams.setDescription("Fenius reservation");
        rparams.setStartTime(startTime);
        rparams.setEndTime(endTime);
        rparams.setSrc(resvParams.getSrcEndpoint());
        rparams.setDst(resvParams.getDstEndpoint());
        rparams.setSrcVlan(resvParams.getSrcVlanTag());
        rparams.setDstVlan(resvParams.getDstVlanTag());
        rparams.setLayer("2");
        rparams.setPathSetupMode("timer-automatic");
        log.debug("src: "+resvParams.getSrcEndpoint());
        log.debug("dst: "+resvParams.getDstEndpoint());
        log.debug("start: "+rparams.getStartTime());
        log.debug("end: "+rparams.getEndTime());

        // pathInfo.setPathType("requested");

        CreateClient cl = new CreateClient();
        cl.configureSoap();
        ResCreateContent createReservation = null;
        try {
            createReservation = cl.formRequest(rparams);
        } catch (Exception ex) {
            FInternalFault fi = new FInternalFault();
            fi.setMessage(ex.getMessage());
            throw fi;
        }


        CreateReply createReply = null;
        synchronized(lockObject) {
            try {
                log.debug("Calling create() on IDC...");
                createReply = cl.performRequest(createReservation);
            } catch (Exception e) {
                e.printStackTrace(System.err);
                throw new FExternalFault(e);
            }
        }
        log.debug("IDC responded");


        FCreateResp resp = new FCreateResp();
        FReservation respResvParams = new FReservation();
        resp.setResvParams(respResvParams);

        respResvParams.setActivationTime(resvParams.getActivationTime());
        respResvParams.setDuration(resvParams.getDuration());
        respResvParams.setBandwidth(resvParams.getBandwidth());
        respResvParams.setSrcEndpoint(resvParams.getSrcEndpoint());
        respResvParams.setDstEndpoint(resvParams.getDstEndpoint());
        respResvParams.setSrcVlanTag(resvParams.getSrcVlanTag());
        respResvParams.setDstVlanTag(resvParams.getDstVlanTag());
        respResvParams.setReservationId(createReply.getGlobalReservationId());

        log.debug("Waiting for 10 seconds for IDC to update status...");
        try {
            Thread.sleep(10000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

        synchronized(lockObject) {
            QueryClient qcl = new QueryClient();
            qcl.configureSoap();
            log.debug("Getting new status from IDC...");
            ResDetails rd = null;
            try {
                rd = qcl.query(createReply.getGlobalReservationId());
                log.info("OSCARS status for "+createReply.getGlobalReservationId()+" : "+rd.getStatus());
                this.setStatuses(respResvParams, rd.getStatus());
            } catch (Exception e) {
                log.error(e);
                log.error("Error getting status: ");
                e.printStackTrace();
                respResvParams.setReservationStatus(FReservationStatus.FAILED);
                respResvParams.setActivationStatus(FActivationStatus.FAILED);
            }
            
            log.debug("Create completed, returning response to client");
        }
        return resp;
    }



    public FListResp list(FListReq r) throws FInternalFault, FExternalFault {
        FListResp resp = new FListResp();
        ArrayList<FReservation> reservations = new ArrayList<FReservation>();

        Long now = System.currentTimeMillis();
        Long timeout = 60000L;
        ReservationsCache cache = ReservationsCache.getInstance();
        if (cache.getTimestamp() < now - timeout) {
            reservations = this.fetchReservations();
            cache.setReservations(reservations);
            cache.setTimestamp(now);
        } else {
            reservations = cache.getReservations();
        }
        resp.setReservations(reservations);
        return resp;
    }

    
    
    
    public FCancelResp cancel(FCancelReq r) throws FInternalFault, FExternalFault {
        String gri = r.getReservationId();
        log.debug("Cancel received for gri:"+gri);
        CancelClient cl = new CancelClient();
        FCancelResp fresp = new FCancelResp();
        synchronized(lockObject) {
            cl.configureSoap();
            try {
                String response = cl.cancel(gri);
                log.debug("Cancel response from IDC gri:"+gri+": "+response);
                fresp.setReservationStatus(FReservationStatus.RELEASING);
            } catch (Exception e) {
                log.error(e);
                log.error("Error canceling: ");
                e.printStackTrace();
                throw new FExternalFault(e);
            }
        }
        return fresp;
        
    }
    

    public FQueryResp query(FQueryReq r) throws FInternalFault, FExternalFault {
        log.debug("Query received");
        String gri = r.getReservationId();
        log.debug("Query received for gri:"+gri);
        QueryClient cl = new QueryClient();
        FQueryResp fresp = new FQueryResp();
        synchronized(lockObject) {
            cl.configureSoap();
            try {
                ResDetails resv = cl.query(gri);
                FReservation reservation = this.makeFResv(resv);
                fresp.setResvParams(reservation);
            } catch (Exception e) {
                log.error(e);
                log.error("Error querying: ");
                e.printStackTrace();
                throw new FExternalFault(e);
            }
        }
        return fresp;
    }

    
    
    
    // TODO
    public FAvailResp isAvailable(FAvailReq r) throws FInternalFault,
            FExternalFault {
        throw new FInternalFault("isAvailable() not implemented on IDCTranslator");
    }


    private FReservation makeFResv(ResDetails resv) {
        FReservation reservation = new FReservation();
        
        log.debug(resv.getGlobalReservationId());
        reservation.setBandwidth(resv.getBandwidth());
        Date activationTime = new Date(resv.getStartTime()*1000);
        Long duration = resv.getEndTime() - resv.getStartTime();
        reservation.setActivationTime(activationTime);
        log.info("OSCARS status for "+resv.getGlobalReservationId()+" : "+resv.getStatus());

        this.setStatuses(reservation, resv.getStatus());
        reservation.setReservationId(resv.getGlobalReservationId());
        reservation.setDuration(duration.intValue());
        String srcEndpoint = "";
        String dstEndpoint = "";
        if (resv.getPathInfo().getLayer2Info() != null) {
            srcEndpoint = resv.getPathInfo().getLayer2Info().getSrcEndpoint();
            dstEndpoint = resv.getPathInfo().getLayer2Info().getDestEndpoint();
        } else if (resv.getPathInfo().getLayer3Info() != null) {
            srcEndpoint = resv.getPathInfo().getLayer3Info().getSrcHost();
            dstEndpoint = resv.getPathInfo().getLayer3Info().getDestHost();
        }

        String vlanId = "";
        PathInfo pathInfo = resv.getPathInfo();
        CtrlPlanePathContent path = pathInfo.getPath();
        CtrlPlaneHopContent[] hops = path.getHop();
        if (hops != null) {
            for (CtrlPlaneHopContent hop : hops) {
               CtrlPlaneLinkContent link = hop.getLink();
               if (link==null) {
                   continue;
               }
               CtrlPlaneSwcapContent swcap = link.getSwitchingCapabilityDescriptors();
               CtrlPlaneSwitchingCapabilitySpecificInfo swcapInfo = swcap.getSwitchingCapabilitySpecificInfo();
               if("ethernet".equals(swcap.getEncodingType())){
                   String vlanRange = swcapInfo.getVlanRangeAvailability();
                   if (vlanRange != null && !"".equals(vlanRange) ) {
                       log.debug("VLAN: "+vlanRange);
                       vlanId = vlanRange;
                       reservation.setSrcVlanTag(vlanId);
                       reservation.setDstVlanTag(vlanId);
                   }
               }
            }
        }


        reservation.setSrcEndpoint(srcEndpoint);
        reservation.setDstEndpoint(dstEndpoint);
        
        return reservation;
    }
    


    private ArrayList<FReservation> fetchReservations() throws FExternalFault {

        ArrayList<FReservation> reservations = new ArrayList<FReservation>();

        ListClient cl = new ListClient();
        ResDetails[] resvs = null;
        synchronized(lockObject) {
            cl.configureSoap();
            cl.configure();
            try {
                ListReply listResp = cl.performRequest(cl.formRequest());
                resvs = cl.filterResvs(listResp.getResDetails());
            } catch (Exception e) {
                log.error(e);
                log.error("Error listing reservations: ");
                e.printStackTrace();
                throw new FExternalFault(e);
            }
        }
        for (ResDetails resv : resvs) {
            FReservation reservation = this.makeFResv(resv);

            reservations.add(reservation);
        }
        return reservations;
    }
    private void setStatuses(FReservation respResvParams, String oscarsStatus) {
        respResvParams.setActivationStatus(FActivationStatus.INACTIVE);
        if (oscarsStatus.equals("ACCEPTED")) {
            respResvParams.setReservationStatus(FReservationStatus.RESERVING);
            respResvParams.setActivationStatus(FActivationStatus.INACTIVE);
        }
        if (oscarsStatus.equals("ACTIVE")) {
            respResvParams.setReservationStatus(FReservationStatus.RESERVED);
            respResvParams.setActivationStatus(FActivationStatus.ACTIVE);
        }
        if (oscarsStatus.equals("PENDING")) {
            respResvParams.setReservationStatus(FReservationStatus.RESERVED);
            respResvParams.setActivationStatus(FActivationStatus.INACTIVE);
        }
        if (oscarsStatus.equals("INCREATE")) {
            respResvParams.setReservationStatus(FReservationStatus.RESERVED);
            respResvParams.setActivationStatus(FActivationStatus.ACTIVATING);
        }
        if (oscarsStatus.equals("INTEARDOWN")) {
            respResvParams.setReservationStatus(FReservationStatus.RESERVED);
            respResvParams.setActivationStatus(FActivationStatus.DEACTIVATING);
        }
        if (oscarsStatus.equals("CANCELLED")) {
            respResvParams.setReservationStatus(FReservationStatus.RELEASED);
            respResvParams.setActivationStatus(FActivationStatus.INACTIVE);
        }
        if (oscarsStatus.equals("INCANCEL")) {
            respResvParams.setReservationStatus(FReservationStatus.RELEASING);
            respResvParams.setActivationStatus(FActivationStatus.INACTIVE);
        }
        if (oscarsStatus.equals("FINISHED")) {
            respResvParams.setReservationStatus(FReservationStatus.FINISHED);
            respResvParams.setActivationStatus(FActivationStatus.INACTIVE);
        }
    }
    
    private void loadConfig() throws FInternalFault {
        System.err.println("loadConfig start");

        System.err.println("loadConfig end");
    }

}
